#conding:utf-8
import pandas as pd
import numpy as np
# todo 和Spark中的dataframe进行理解，但是没有spark中的功能强大
'''
Pandas 数据结构 - DataFrame
DataFrame 是 Pandas 中的另一个核心数据结构，用于表示二维表格型数据。

DataFrame 是一个表格型的数据结构，它含有一组有序的列，每列可以是不同的值类型（数值、字符串、布尔型值）。

DataFrame 既有行索引也有列索引，它可以被看做由 Series 组成的字典（共同用一个索引）。

DataFrame 提供了各种功能来进行数据访问、筛选、分割、合并、重塑、聚合以及转换等操作。

DataFrame 特点：

二维结构： DataFrame 是一个二维表格，可以被看作是一个 Excel 电子表格或 SQL 表，具有行和列。可以将其视为多个 Series 对象组成的字典。

列的数据类型： 不同的列可以包含不同的数据类型，例如整数、浮点数、字符串或 Python 对象等。

索引：DataFrame 可以拥有行索引和列索引，类似于 Excel 中的行号和列标。

大小可变：可以添加和删除列，类似于 Python 中的字典。

自动对齐：在进行算术运算或数据对齐操作时，DataFrame 会自动对齐索引。

处理缺失数据：DataFrame 可以包含缺失数据，Pandas 使用 NaN（Not a Number）来表示。

数据操作：支持数据切片、索引、子集分割等操作。

时间序列支持：DataFrame 对时间序列数据有特别的支持，可以轻松地进行时间数据的切片、索引和操作。

丰富的数据访问功能：通过 .loc、.iloc 和 .query() 方法，可以灵活地访问和筛选数据。

灵活的数据处理功能：包括数据合并、重塑、透视、分组和聚合等。

数据可视化：虽然 DataFrame 本身不是可视化工具，但它可以与 Matplotlib 或 Seaborn 等可视化库结合使用，进行数据可视化。

高效的数据输入输出：可以方便地读取和写入数据，支持多种格式，如 CSV、Excel、SQL 数据库和 HDF5 格式。

描述性统计：提供了一系列方法来计算描述性统计数据，如 .describe()、.mean()、.sum() 等。

灵活的数据对齐和集成：可以轻松地与其他 DataFrame 或 Series 对象进行合并、连接或更新操作。

转换功能：可以对数据集中的值进行转换，例如使用 .apply() 方法应用自定义函数。

滚动窗口和时间序列分析：支持对数据集进行滚动窗口统计和时间序列分析。

DataFrame 构造方法如下：

pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
参数说明：

data：DataFrame 的数据部分，可以是字典、二维数组、Series、DataFrame 或其他可转换为 DataFrame 的对象。如果不提供此参数，则创建一个空的 DataFrame。
index：DataFrame 的行索引，用于标识每行数据。可以是列表、数组、索引对象等。如果不提供此参数，则创建一个默认的整数索引。
columns：DataFrame 的列索引，用于标识每列数据。可以是列表、数组、索引对象等。如果不提供此参数，则创建一个默认的整数索引。
dtype：指定 DataFrame 的数据类型。可以是 NumPy 的数据类型，例如 np.int64、np.float64 等。如果不提供此参数，则根据数据自动推断数据类型。
copy：是否复制数据。默认为 False，表示不复制数据。如果设置为 True，则复制输入的数据。
Pandas DataFrame 是一个二维的数组结构，类似二维数组。
'''
# 实例 - 使用列表创建


data = [['Google', 10], ['Runoob', 12], ['Wiki', 13]]

# 创建DataFrame
df = pd.DataFrame(data, columns=['Site', 'Age'])

# 使用astype方法设置每列的数据类型
df['Site'] = df['Site'].astype(str)
df['Age'] = df['Age'].astype(float)

print(df)



# 实例 - 使用字典创建 直接给列名，将列按照顺序输出


data = {'Site':['Google', 'Runoob', 'Wiki'], 'Age':[10, 12, 13]}

df = pd.DataFrame(data)

print (df)




# 实例 - 使用 ndarrays 创建



# 创建一个包含网站和年龄的二维ndarray
ndarray_data = np.array([
    ['Google', 10],
    ['Runoob', 12],
    ['Wiki', 13]
])

# 使用DataFrame构造函数创建数据帧
df = pd.DataFrame(ndarray_data, columns=['Site', 'Age'])

# 打印数据帧
print(f'打印数据帧:{df}')




# 实例 - 使用字典创建(还是有顺序的)


data = [{'a': 1, 'b': 2},{'a': 5, 'b': 10, 'c': 20}]
data = [{'a': 5, 'b': 10, 'c': 20},{'a': 1, 'b': 2}]

df = pd.DataFrame(data)

print(f'使用字典创建:\n{df}')



print('------------------------------------分割线------------------------------------')

# Pandas 可以使用 loc 属性返回指定行的数据，如果没有设置索引，第一行索引为 0，第二行索引为 1，以此类推：



data = {
    "calories": [420, 380, 390],
    "duration": [50, 40, 45]
}

# 数据载入到 DataFrame 对象
df = pd.DataFrame(data)
# 注意：返回结果其实就是一个 Pandas Series 数据。
# 返回第一行
print(df.loc[0])
# 返回第二行
print(df.loc[1])


# 也可以返回多行数据，使用 [[ ... ]] 格式，... 为各行的索引，以逗号隔开：
# 返回结果其实就是一个 Pandas DataFrame 数据。


data = {
  "calories": [420, 380, 390],
  "duration": [50, 40, 45]
}

# 数据载入到 DataFrame 对象
df = pd.DataFrame(data)

# 返回第一行和第二行
print(df.loc[[0, 1]])



# 我们可以指定索引值，如下实例：



data = {
  "calories": [420, 380, 390],
  "duration": [50, 40, 45]
}

df = pd.DataFrame(data, index = ["day1", "day2", "day3"])

print(df)



# Pandas 可以使用 loc 属性返回指定索对应到某一行：




data = {
  "calories": [420, 380, 390],
  "duration": [50, 40, 45]
}

df = pd.DataFrame(data, index = ["day1", "day2", "day3"])

# 指定索引
print(df.loc["day2"])


print('--------------------------创建 DataFrame-----------------------------------')




print(str('通过字典创建 DataFrame:\n'),  pd.DataFrame({'Column1': [1, 2, 3], 'Column2': [4, 5, 6]}))

print('从列表的列表创建：外层列表代表行，内层列表代表列\n' , pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]],
                  columns=['Column1', 'Column2', 'Column3']))
print('从 NumPy 数组创建：提供一个二维 NumPy 数组。\n' , pd.DataFrame(pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))))


# 从 Series 创建 DataFrame：通过 pd.Series() 创建。
# 从 Series 创建 DataFrame
s1 = pd.Series(['Alice', 'Bob', 'Charlie'])
s2 = pd.Series([25, 30, 35])
s3 = pd.Series(['New York', 'Los Angeles', 'Chicago'])
df = pd.DataFrame({'Name': s1, 'Age': s2, 'City': s3})
print('从 Series 创建 DataFrame\n' , df)



print('--------------------------DataFrame 的属性和方法------------------------')
df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# DataFrame 对象有许多属性和方法，用于数据操作、索引和处理，例如：shape、columns、index、head()、tail()、info()、describe()、mean()、sum() 等。
# DataFrame 的属性和方法
print('形状:',df.shape)     # 形状
print('列名:',df.columns)   # 列名
print('索引:',df.index)     # 索引
print('前几行数据，默认是前 5 行:',df.head())    # 前几行数据，默认是前 5 行
print('后几行数据，默认是后 5 行:',df.tail())    # 后几行数据，默认是后 5 行
print('数据信息:',df.info())    # 数据信息
print('描述统计信息:',df.describe())# 描述统计信息
print('求平均值:',df.mean())    # 求平均值
print('求和:',df.sum())     # 求和



print('-------------------------访问 DataFrame 元素--------------------------')
df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]],
                columns=['Column1', 'Column2', 'Column3'])
# 通过列名访问
print(df['Column1'])

# 通过属性访问
print(df.Column1)

# 通过 .loc[] 访问
print(df.loc[:, 'Column1'])

# 通过 .iloc[] 访问
print(df.iloc[:, 0])  # 假设 'Column1' 是第一列

# 访问单个元素
print(df['Column1'][0])

# 通过行标签访问
print(df.loc[0, 'Column1'])


print('-----------------------------修改 DataFrame-----------------------------')

# 修改列数据：直接对列进行赋值。

df['Column1'] = [10, 11, 12]
# 添加新列：给新列赋值。

df['NewColumn'] = [100, 200, 300]


# 添加新行：使用 loc、append 或 concat 方法。
# 使用 loc 为特定索引添加新行
df.loc[3] = [13, 14, 15, 16]

# 使用 append 添加新行到末尾
# new_row = {'Column1': 13, 'Column2': 14, 'NewColumn': 16}
# df = df.append(new_row, ignore_index=True)
'''
注意：append() 方法在 pandas 版本 1.4.0 中已经被标记为弃用，并将在未来的版本中被移除，官方推荐使用 concat() 作为替代方法来进行数据的合并操作。

concat() 方法用于合并两个或多个 DataFrame，当你想要添加一行到另一个 DataFrame 时，可以将新行作为一个新的 DataFrame，然后使用 concat()：
'''
# 使用concat添加新行
new_row = pd.DataFrame([[4, 7]], columns=['A', 'B'])  # 创建一个只包含新行的DataFrame,最新一行其他都是NaN 最后AB行是添加的
df = pd.concat([df, new_row], ignore_index=True)  # 将新行添加到原始DataFrame

print(df)

# print('-----------------------------------删除 DataFrame 元素-------------------------------')

# 删除列：使用 drop 方法。
'''
axis=0：针对行进行操作，例如删除行或计算每列的总和。
axis=1：针对列进行操作，例如删除列或计算每行的总和。
'''
print('删除指定列之前：\n',df)
# df_dropped = df.drop('Column1', axis=1)
# 删除索引为 0 的行,下面两个可以
# df_dropped = df.drop(0, axis=0)

# df_dropped = df.drop(0)


# print('删除指定列之后：\n',df_dropped)
# print('-----------------------------------DataFrame 的统计分析-------------------------------')

# 描述性统计：使用 .describe() 查看数值列的统计摘要。

df.describe()
# 计算统计数据：使用聚合函数如 .sum()、.mean()、.max() 等。

df['Column1'].sum()
df.mean()


# 转换数据类型：使用 astype 方法。
df['Column1'] = df['Column1'].astype('float64')





print('--------------------------------DataFrame 的合并与分割是--------------------------')

# 在 `pandas` 中，DataFrame 的合并与分割是数据操作中的常见任务。以下是一些常用的方法：

### 合并 DataFrame

# `pandas` 提供了几种方法来合并 DataFrame，包括 `concat`、`merge` 和 `join`。

#### 1. `concat`

# `concat` 方法用于沿指定轴连接多个 DataFrame。



# 创建两个示例 DataFrame
df1 = pd.DataFrame({
    'A': ['A0', 'A1', 'A2'],
    'B': ['B0', 'B1', 'B2']
})

df2 = pd.DataFrame({
    'A': ['A3', 'A4', 'A5'],
    'B': ['B3', 'B4', 'B5']
})

# 沿行（axis=0）连接
result = pd.concat([df1, df2], axis=0)
print("沿行连接的结果:")
print(result)

# 沿列（axis=1）连接
result = pd.concat([df1, df2], axis=1)
print("\n沿列连接的结果:")
print(result)


#### 2. `merge`

# `merge` 方法用于根据一个或多个键将两个 DataFrame 合并，类似于 SQL 中的 JOIN 操作。

# 创建两个示例 DataFrame
df1 = pd.DataFrame({
    'key': ['K0', 'K1', 'K2'],
    'A': ['A0', 'A1', 'A2']
})

df2 = pd.DataFrame({
    'key': ['K0', 'K1', 'K3'],
    'B': ['B0', 'B1', 'B3']
})

# 根据 'key' 列进行合并（内连接）
result = pd.merge(df1, df2, on='key', how='inner')
print("\n内连接的结果:")
print(result)

#### 3. `join`

# `join` 方法用于在索引上合并两个 DataFrame，类似于 `merge` 方法，但更适用于基于索引的合并。

# 创建两个示例 DataFrame
df1 = pd.DataFrame({
    'A': ['A0', 'A1', 'A2']
}, index=['K0', 'K1', 'K2'])

df2 = pd.DataFrame({
    'B': ['B0', 'B1', 'B2']
}, index=['K0', 'K2', 'K3'])

# 使用索引进行合并（外连接）
result = df1.join(df2, how='outer')
print("\n外连接的结果:")
print(result)

### 分割 DataFrame

# 分割 DataFrame 通常使用 `iloc` 和 `loc` 方法，或者 `split` 方法。

#### 1. `iloc`

# `iloc` 基于位置索引进行分割。

# 创建一个示例 DataFrame
df = pd.DataFrame({
    'A': ['A0', 'A1', 'A2', 'A3'],
    'B': ['B0', 'B1', 'B2', 'B3']
})

# 分割前两行
result = df.iloc[:2]
print("\n前两行的结果:")
print(result)

#### 2. `loc`

# `loc` 基于标签索引进行分割。

# 分割 'A' 列
result = df.loc[:, 'A']
print("\n'A' 列的结果:")
print(result)

#### 3. `split`

# 虽然 `pandas` 本身没有专门的 `split` 方法，但可以通过一些技巧来分割 DataFrame。例如，基于某列的值进行分割：

# 创建一个示例 DataFrame
df = pd.DataFrame({
    'A': ['foo', 'bar', 'foo', 'bar'],
    'B': ['one', 'one', 'two', 'two'],
    'C': [1, 2, 3, 4]
})

# 根据 'A' 列的值进行分割
grouped = df.groupby('A')
for name, group in grouped:
    print(f"\n组 {name} 的结果:")
    print(group)

### 总结

# - **合并**：使用 `concat`、`merge` 和 `join` 方法。
# - **分割**：使用 `iloc`、`loc` 方法，或通过 `groupby` 进行特定列的分割。

